﻿namespace Marc.JwOrgClient.ViewModels
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.IO;
    using System.Linq;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Windows.Input;
    using GalaSoft.MvvmLight;
    using GalaSoft.MvvmLight.Command;
    using GalaSoft.MvvmLight.Messaging;
    using GalaSoft.MvvmLight.Threading;
    using Marc.JwOrgClient.Messages;
    using Marc.JwOrgClient.Model;
    using Marc.JwOrgClient.Util;

    public class PodcastDownloadViewModel : ViewModelBase
    {
        private readonly ICommand _cancelDownloadCommand;
        private readonly ObservableCollection<ChapterViewModel> _chapters = new ObservableCollection<ChapterViewModel>();
        private readonly ICommand _downloadCommand;
        private readonly CancellationTokenSource _downloadTaskCancellationTokenSource = new CancellationTokenSource();

        private readonly IEnumerable<MagazineType> _magazineTypes = new[] {
            MagazineType.Awake,
            MagazineType.Watchtower,
            MagazineType.WatchtowerStudyEdition,
        };

        private readonly IEnumerable<IPodcastNamingStrategy> _namingStrategies = new IPodcastNamingStrategy[] {
            new HierarchicalNamingStrategy(),
            new FlatNamingStrategy(),
        };

        private readonly ICommand _openBrowseDialog;
        private readonly HashSet<MagazineType> _selectedMagazineTypes = new HashSet<MagazineType>();
        private readonly ICommand _toggleMagazinSelectionCommand;

        private string _currentDownload;
        private double _downloadProgress;
        private Task _downloadTask;
        private bool _isDownloading;
        private string _rootPath;
        private Language _selectedLanguage;
        private IPodcastNamingStrategy _selectedNamingStrategy;

        public PodcastDownloadViewModel()
        {
            this._downloadCommand = new RelayCommand(
                () => {
                    this.IsDownloading = true;
                    this.DownloadProgress = 0d;
                    this._downloadTask = new Task(
                        () => {
                            ChapterViewModel[] chaptersToDownload = this.Chapters.Where(ch => !ch.IsDownloaded).ToArray();
                            for (int index = 0; index < chaptersToDownload.Length; index++)
                            {
                                if (this._downloadTaskCancellationTokenSource.Token.IsCancellationRequested)
                                {
                                    return;
                                }
                                this.DownloadProgress = (double)index / chaptersToDownload.Length * 100;
                                this.CurrentDownload = chaptersToDownload[index].Chapter.Title;
                                ChapterViewModel chapter = chaptersToDownload[index];
                                chapter.Download();
                            }
                        }, this._downloadTaskCancellationTokenSource.Token);
                    this._downloadTask.Start();
                    this._downloadTask.ContinueWith(task => { this.IsDownloading = false; });
                },
                () => this._chapters.Any(ch => !ch.IsDownloaded && Directory.Exists(this.RootPath)));

            this._cancelDownloadCommand = new RelayCommand(
                () => {
                    if (this._downloadTask != null && !this._downloadTask.IsCanceled)
                    {
                        this._downloadTaskCancellationTokenSource.Cancel();
                    }
                });

            this._openBrowseDialog = new RelayCommand(
                () => {
                    string initialFolder = Directory.Exists(this.RootPath) ? this.RootPath : Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
                    Messenger.Default.Send(new ShowFolderBrowserDialogMessage(initialFolder, selectedFolder => { this.RootPath = selectedFolder; }));
                });

            this._toggleMagazinSelectionCommand = new RelayCommand<MagazineType>(
                mt => {
                    if (this._selectedMagazineTypes.Contains(mt))
                    {
                        this._selectedMagazineTypes.Remove(mt);
                        foreach (ChapterViewModel chapter in this.Chapters.Where(ch => ch.Chapter.Magazine.Type == mt).ToArray())
                        {
                            this.Chapters.Remove(chapter);
                        }
                        CommandManager.InvalidateRequerySuggested();
                    }
                    else
                    {
                        this._selectedMagazineTypes.Add(mt);
                        var podcast = new Podcast(this.SelectedLanguage, mt);
                        podcast.DownloadMetadata(
                            () => {
                                foreach (PodcastMagazineChapter chapter in podcast.Magazines.SelectMany(m => m.Chapters).OrderBy(ch => ch.Magazine.Date).ThenBy(ch => ch.Filename))
                                {
                                    PodcastMagazineChapter c = chapter;
                                    DispatcherHelper.RunAsync(() => this.Chapters.Add(new ChapterViewModel(this.RootPath, this._selectedNamingStrategy, c)));
                                    CommandManager.InvalidateRequerySuggested();
                                }
                            });
                    }
                });

            this.SelectedNamingStrategy = this.NamingStrategies.First();
            this.SelectedLanguage = this.Languages.First();
            this.RootPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyMusic), "jw.org Podcasts");
        }

        public ICommand CancelDownloadCommand
        {
            get { return this._cancelDownloadCommand; }
        }

        public ObservableCollection<ChapterViewModel> Chapters
        {
            get { return this._chapters; }
        }

        public string CurrentDownload
        {
            get { return this._currentDownload; }
            set
            {
                if (value != this._currentDownload)
                {
                    this._currentDownload = value;
                    RaisePropertyChanged(() => this.CurrentDownload);
                }
            }
        }

        public ICommand DownloadCommand
        {
            get { return this._downloadCommand; }
        }

        public double DownloadProgress
        {
            get { return this._downloadProgress; }
            set
            {
                this._downloadProgress = value;
                RaisePropertyChanged(() => this.DownloadProgress);
            }
        }

        public bool IsDownloading
        {
            get { return this._isDownloading; }
            set
            {
                if (value != this._isDownloading)
                {
                    this._isDownloading = value;
                    RaisePropertyChanged(() => this.IsDownloading);
                }
            }
        }

        public IEnumerable<Language> Languages
        {
            get
            {
                yield return Language.English;
                yield return Language.German;
                yield return Language.Spanish;
            }
        }

        public IEnumerable<MagazineType> MagazineTypes
        {
            get { return this._magazineTypes; }
        }

        public IEnumerable<IPodcastNamingStrategy> NamingStrategies
        {
            get { return this._namingStrategies; }
        }

        public ICommand OpenBrowseDialog
        {
            get { return this._openBrowseDialog; }
        }

        public string RootPath
        {
            get { return this._rootPath; }
            set
            {
                if (value != this._rootPath)
                {
                    this._rootPath = value;
                    this.ApplyNamingStrategyAndRootPathToChapters();
                    RaisePropertyChanged(() => this.RootPath);
                }
            }
        }

        public Language SelectedLanguage
        {
            get { return this._selectedLanguage; }
            set
            {
                if (value != this._selectedLanguage)
                {
                    this._selectedLanguage = value;
                    foreach (MagazineType magazineType in this._selectedMagazineTypes)
                    {
                        this.ToggleMagazinSelectionCommand.Execute(magazineType);
                        this.ToggleMagazinSelectionCommand.Execute(magazineType);
                    }
                    RaisePropertyChanged(() => this.SelectedLanguage);
                }
            }
        }

        public IPodcastNamingStrategy SelectedNamingStrategy
        {
            get { return this._selectedNamingStrategy; }
            set
            {
                if (value != this._selectedNamingStrategy)
                {
                    this._selectedNamingStrategy = value;
                    this.ApplyNamingStrategyAndRootPathToChapters();
                    RaisePropertyChanged(() => this.SelectedNamingStrategy);
                }
            }
        }

        public ICommand ToggleMagazinSelectionCommand
        {
            get { return this._toggleMagazinSelectionCommand; }
        }

        private void ApplyNamingStrategyAndRootPathToChapters()
        {
            foreach (ChapterViewModel chapter in this.Chapters)
            {
                chapter.ChangeNamingStrategy(this.RootPath, this.SelectedNamingStrategy);
            }
            CommandManager.InvalidateRequerySuggested();
        }
    }
}